﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Diagnostics;

namespace devtm.Aop.Event
{



    public enum PolicyEntryEnum
    {
        None,
        CancelEntryWithThrowed,
        CancelEntryWithReturnValue,
        CancelExitWithThrowed,
        CancelExitWithReturnValue
    }

    [Serializable]
    [DebuggerStepThrough]
    public class InterceptEventArgs : EventArgs
    {


        #region fields

        private ContainerProperties c0 = null;
        private StackTrace _Stack;
        public object[] parameters;
        private Dictionary<string, Parameter> _Parameters;
        private Dictionary<object, object> userData;
        private Dictionary<int, ContainerProperties> CustomAttributeProperties { get; set; }
        private Dictionary<int, object> Tags { get; set; }
        
        #endregion

        #region Ctor


        public InterceptEventArgs()
        {

        }

        
        public InterceptEventArgs(StackFrame f, params object[] parameters)
        {

            Key = Guid.NewGuid().ToString();
            this.parameters = parameters;
            Frame = f;
            Member = f.GetMethod();

            CustomAttributeProperties = new Dictionary<int, ContainerProperties>();
            Tags = new Dictionary<int, object>();

            Return = new NotSettedValue();

        }

        #endregion

        #region Properties
        
        public Dictionary<string, Parameter> Parameters
        {
            get
            {
                return _Parameters ?? (_Parameters = ResolveParameter());
            }
        }        


        public ContainerProperties Properties
        {
            get
            {
                
                if (!CustomAttributeProperties.ContainsKey(Context))
                    CustomAttributeProperties.Add(Context, new ContainerProperties(Context));

                return CustomAttributeProperties[Context];

            }
        }

        
        public Dictionary<object, object> UserData
        {
            get
            {
                if (this.userData == null)
                {
                    this.userData = new Dictionary<object, object>();
                }
                return this.userData;
            }
        }



        public object Tag
        {
            get
            {                
                return Tags[Context];
            }

            set
            {
                if (!Tags.ContainsKey(Context))
                    Tags.Add(Context, value);
                else
                {
                    Tags[Context] = value;
                }
            }
        }


        public string Key { get; private set; }
        public MethodBase Member { get; set; }
        public Object Return { get; set; }
        public StackTrace Stack { get { return _Stack; } }
        public StackFrame Frame { get; set; }
        public int FrameIndex { get; set; }
        public PolicyEntryEnum PolicyEntry { get; set; }
        public int Context { get; set; }
        

        private Exception _exception;
        public Exception Exception 
        {
            get { return _exception; }
            set
            {
                _exception = value;
                _Stack = new StackTrace(Exception);
                
            }
        }

        #endregion

        #region Methods

        public void AddProperty(int key1, string name, object value)
        {

            ContainerProperties c1 = c0;

            if (c1 == null || c1.Key != key1)
            {

                if (!CustomAttributeProperties.ContainsKey(key1))
                {
                    c1 = new ContainerProperties(key1);
                    CustomAttributeProperties.Add(key1, c1);
                }
                else                
                    c1 = CustomAttributeProperties[key1];
                
            }

            c1.Add(name, value);

            c0 = c1;

        }

        private Dictionary<string, Parameter> ResolveParameter()
        {
            Dictionary<string, Parameter> lst = new Dictionary<string, Parameter>();

            int index = 0;
            foreach (ParameterInfo p in Member.GetParameters())
            {

                Way way = Way.In;
                if (p.IsOut)
                    way = Way.Out;
                else if (p.ParameterType.IsByRef)
                    way = Way.Ref;

                lst.Add(p.Name, new Parameter(p.Name, p.ParameterType, parameters[index], way));
            }

            return lst;
        }
                

        #endregion



        public object Invoke(object instance)
        {
            return Member.Invoke(instance, parameters);
        }



        public bool IsVoid 
        { 
            get
            {
                return (Member as MethodInfo).ReturnType == typeof(void);
            }
        }


        internal string ArgumentsToString()
        {
            return "";
        }
    }
}
